package com.corpgovernment.organization.service.budget;

import com.corpgovernment.api.operatelog.soa.request.AddOperateLogRequestVo;
import com.corpgovernment.api.operatelog.vo.OperateLogContentDataVo;
import com.corpgovernment.common.base.BaseUserInfo;
import com.corpgovernment.common.base.CommonException;
import com.corpgovernment.common.common.CorpBusinessException;
import com.corpgovernment.common.utils.BaseUtils;
import com.corpgovernment.common.utils.DateUtil;
import com.corpgovernment.common.utils.ExecutorUtil;
import com.corpgovernment.organization.constant.OrganizationResponseCodeEnum;
import com.corpgovernment.organization.entity.db.MbOrgInfo;
import com.corpgovernment.organization.entity.db.budget.*;
import com.corpgovernment.organization.enumm.budget.BudgetOperationEnum;
import com.corpgovernment.organization.enumm.budget.BudgetScopeEnum;
import com.corpgovernment.organization.enumm.budget.BudgetTypeEnum;
import com.corpgovernment.organization.mapper.budget.*;
import com.corpgovernment.organization.mq.MultiTenantBudgetNotifyProducer;
import com.corpgovernment.organization.service.IOrganizationInfoService;
import com.corpgovernment.organization.util.BudgetAlarmUtil;
import com.corpgovernment.organization.util.BudgetDateUtil;
import com.corpgovernment.organization.util.PreCheckUtil;
import com.corpgovernment.organization.vo.budget.*;
import com.corpgovernment.redis.cache.UserCacheManager;
import com.ctrip.corp.obt.generic.core.context.UserInfoContext;
import com.ctrip.corp.obt.generic.utils.CollectionUtils;
import com.ctrip.corp.obt.generic.utils.StringUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class AddBudgetService {

    @Autowired
    private BudgetInfoMapper budgetInfoMapper;
    @Autowired
    private BudgetGroupMapper budgetGroupMapper;
    @Autowired
    private BudgetNotifyMapper budgetNotifyMapper;
    @Autowired
    private BudgetItemMapper budgetItemMapper;
    @Autowired
    private BudgetRecordMapper budgetRecordMapper;
    @Autowired
    private MultiTenantBudgetNotifyProducer multiTenantBudgetNotifyProducer;
    @Autowired
    private UserCacheManager userCacheManager;
    @Autowired
	private IOrganizationInfoService organizationInfoService;
    @Autowired
    private BudgetWarningMapper budgetWarningMapper;

    private final static Map<String, String> orderMap = new HashMap(){{
        put("intlhotel","海外酒店");
        put("train","火车");
        put("intlflight","国际机票");
        put("flight","国内机票");
        put("car","打车");
        put("hotel","国内酒店");
    }};
    /**
     * 新增
     */
    @Transactional
    public void addOrUpdateBudget(AddBudgetVo addBudgetVo, BaseUserInfo userInfo) {
        validate(addBudgetVo);
        String budgetId = addBudgetVo.getType().equals(BudgetOperationEnum.ADD.getCode()) ? BaseUtils.getUUID() : addBudgetVo.getId();
        saveBudgetInfoEntity(budgetId, userInfo.getUid(), addBudgetVo, addBudgetVo.getType());
        saveBudgetGroupEntity(budgetId, addBudgetVo, addBudgetVo.getType());
        saveBudgetNotify(budgetId, addBudgetVo.isSendMsg(), addBudgetVo.getNotifyScope(), addBudgetVo.getType());
        saveBudgetItem(budgetId, addBudgetVo.getBudgetTypeObject(), addBudgetVo.getType());
        //发送预算阀值告警消息
        if (addBudgetVo.getType().equals(BudgetOperationEnum.UPDATE.getCode())) {
            multiTenantBudgetNotifyProducer.sendBudgetNotifyMsg(budgetId);
        }
    }



    public void insertOperatorLog(AddBudgetVo oldBudgetVo, AddBudgetVo newBudgetVo) {
        try {
            //BaseUserInfo baseUserInfo = userCacheManager.getBaseUserInfo();
            BaseUserInfo baseUserInfo = UserInfoContext.getContextParams(BaseUserInfo.class);
            ExecutorUtil.logExecutor.execute(() -> {
                try {
                    AddBudgetLogVo oldBudgetLogVo = convertAddBudgetVoLog(oldBudgetVo);
                    AddBudgetLogVo newBudgetLogVo = convertAddBudgetVoLog(newBudgetVo);
                    //查询公司信息
                    String corpId = oldBudgetVo == null ? newBudgetVo.getCorpId() : oldBudgetVo.getCorpId();
					MbOrgInfo corpInfo = organizationInfoService.findByOrgId(corpId);
                    if (corpInfo != null) {
                        if (oldBudgetLogVo != null) {
                            oldBudgetLogVo.setCorpName(corpInfo.getName());
                        }
                        if (newBudgetLogVo != null) {
                            newBudgetLogVo.setCorpName(corpInfo.getName());
                        }
                    }
                    AddOperateLogRequestVo addOperateLogRequestVo = new AddOperateLogRequestVo();
                    List<OperateLogContentDataVo> operateLogContentDataVos = null;
//                    operateLogContentDataVos = OperateLogUtil.toOperateLogContentList(oldBudgetLogVo, newBudgetLogVo);
//                    operateLogService.addOperateLog(OperateLogContentUtil.toAddOperateLog(baseUserInfo, String.valueOf(newBudgetVo==null?oldBudgetVo.getId():newBudgetVo.getId()), addOperateLogRequestVo,
//                            operateLogContentDataVos, ChooseBusinesstype.Budget));
                } catch (Exception e) {
                    log.error("AddBudgetServiceInsertOperator 异常：{}", e);
                }
            });
        } catch (Exception e) {
            log.error("AddBudgetServiceInsertOperator 线程异常：{}", e);
        }
    }

    public AddBudgetLogVo convertAddBudgetVoLog(AddBudgetVo addBudgetVo) {
        if (addBudgetVo == null) {
            return null;
        }
        AddBudgetLogVo addBudgetVoLog = new AddBudgetLogVo();
        BeanUtils.copyProperties(addBudgetVo, addBudgetVoLog);
        if (!CollectionUtils.isEmpty(addBudgetVo.getDepartment())) {
            addBudgetVoLog.setDepartmentNameList(addBudgetVo.getDepartment().stream().map(x -> x.getDepartName()).collect(Collectors.toList()));
        }
        addBudgetVoLog.setAlarmThresholdStr(addBudgetVo.getAlarmThreshold() == null ? null : addBudgetVo.getAlarmThreshold() + "%");
        addBudgetVoLog.setSendMsgStr(addBudgetVo.isSendMsg() ? "是" : "否");
        List<BudgetTypeVo> budgetTypeVos = addBudgetVo.getBudgetTypeObject();
        if (!CollectionUtils.isEmpty(budgetTypeVos)) {
            AddBudgetLogVo.BudgetTypeVoLog budgetTypeVoLog = null;
            List<AddBudgetLogVo.BudgetTypeVoLog> list = new ArrayList<>();
            for (BudgetTypeVo budgetTypeVo : budgetTypeVos) {
                budgetTypeVoLog = new AddBudgetLogVo.BudgetTypeVoLog();
                BeanUtils.copyProperties(budgetTypeVo, budgetTypeVoLog);
                budgetTypeVoLog.setTypeNames(budgetTypeVo.getTypes().stream().map(x -> x.getName()).collect(Collectors.toList()));
                budgetTypeVoLog.setThresholdStr(budgetTypeVo.getThreshold() + "%");
                list.add(budgetTypeVoLog);
            }
            addBudgetVoLog.setBudgetTypeVoLogs(list);
        }
        return addBudgetVoLog;
    }

    private void validate(AddBudgetVo addBudgetVo) {
        Set<String> typeSet = Sets.newHashSet();
        if (addBudgetVo.getType().equals(BudgetOperationEnum.ADD.getCode())) {
            PreCheckUtil.checkNotEmpty(addBudgetVo.getStartTime(), "新增时开始时间不能为空");
            PreCheckUtil.checkNotEmpty(addBudgetVo.getEndTime(), "新增时结束时间不能为空");
            PreCheckUtil.checkNotEmpty(addBudgetVo.getRangeType(), "新增时预算范围类型不能为空");
            PreCheckUtil.checkNotEmpty(addBudgetVo.getDepartment(), "新增时部门选择结果不能为空");
            PreCheckUtil.checkNotNull(BudgetScopeEnum.getEnumByCode(addBudgetVo.getRangeType()), "预算范围类型错误");
            //对部门去重
            addBudgetVo.setDepartment(addBudgetVo.getDepartment().stream().distinct().collect(Collectors.toList()));
            //保存时再一次校重
            int count = budgetGroupMapper.existsByCorpIdAndOrgIdAndTime(addBudgetVo.getCorpId(), addBudgetVo.getDepartment().stream().map(DepartmentVo::getDepartId).collect(Collectors.toList()),
                    BudgetDateUtil.getFirstDayOfMonth(addBudgetVo.getStartTime()), BudgetDateUtil.getLastDayOfMonth(addBudgetVo.getEndTime()));
            PreCheckUtil.checkArgument(count == 0, "存在已有的配置，请检查。");
        } else if (addBudgetVo.getType().equals(BudgetOperationEnum.UPDATE.getCode())) {
            PreCheckUtil.checkNotEmpty(addBudgetVo.getId(), "追加时主键不能为空");
            //查已有的预算项
            List<BudgetItemEntity> budgetItems = budgetItemMapper.selectByBudgetId(addBudgetVo.getId());
            for (BudgetItemEntity budgetItem : budgetItems) {
                String type = budgetItem.getType();
                typeSet.addAll(Sets.newHashSet(type.split("/")));
            }
        } else {
            throw new CorpBusinessException(OrganizationResponseCodeEnum.ACTION_TYPE_ERROR);
        }
        //预算项类型不能重复，各预算项金额之和等于总金额
        BigDecimal sum = new BigDecimal(0);
        for (BudgetTypeVo budgetTypeVo : addBudgetVo.getBudgetTypeObject()) {
            sum = sum.add(budgetTypeVo.getAmount());
            if (budgetTypeVo.getId() == null) {
                List<String> types = Lists.newArrayList();
                if (budgetTypeVo.isIfAll()) {
                    types.addAll(Arrays.stream(BudgetTypeEnum.values()).map(BudgetTypeEnum::getType).collect(Collectors.toList()));
                } else {
                    if (CollectionUtils.isEmpty(budgetTypeVo.getTypes())) {
                        throw new CorpBusinessException(OrganizationResponseCodeEnum.BUDGET_TYPE_IS_NULL);
                    }
                    types.addAll(budgetTypeVo.getTypes().stream().map(TypeDetailVo::getType).collect(Collectors.toList()));
                }
                types.forEach(t -> {
                    if (typeSet.contains(t)) {
                        throw new CorpBusinessException(OrganizationResponseCodeEnum.BUDGET_TYPE_EXISTS);
                    }
                    typeSet.add(t);
                });
            }
            //如果是全部项，阀值设置为同预算总阀值
            if (addBudgetVo.getBudgetTypeObject().size() == 1 && budgetTypeVo.isIfAll()) {
                budgetTypeVo.setThreshold(addBudgetVo.getAlarmThreshold());
            }
        }
        PreCheckUtil.checkArgument(sum.equals(addBudgetVo.getBudgetAmount()), "各预算项金额之和不等于总金额");
    }

    private void saveBudgetInfoEntity(String budgetId, String uid, AddBudgetVo addBudgetVo, String code) {
        if (code.equals(BudgetOperationEnum.UPDATE.getCode())) {
            BudgetInfoEntity budgetInfo = budgetInfoMapper.selectByPrimaryKey(budgetId);
            //记录追加表，总金额有变化才记录
            if (addBudgetVo.getBudgetAmount().compareTo(budgetInfo.getAmount()) != 0) {
                BudgetRecordEntity budgetRecord = new BudgetRecordEntity();
                budgetRecord.setBudgetId(budgetId);
                budgetRecord.setAmount(addBudgetVo.getBudgetAmount().subtract(budgetInfo.getAmount()));
                budgetRecordMapper.insertBatchSelective(Lists.newArrayList(budgetRecord));
            }
            //更新主表
            budgetInfo.setAmount(addBudgetVo.getBudgetAmount());
            budgetInfo.setWarn(addBudgetVo.getAlarmThreshold());
            budgetInfo.setSendMsg(addBudgetVo.isSendMsg());
            //总金额或阀值有变化时，重置告警标记
            if (!BudgetAlarmUtil.isAlarm(budgetInfo.getAmount(), budgetInfo.getAmountUsed(), budgetInfo.getAmountOccupy(), budgetInfo.getWarn())) {
                budgetInfo.setIsNotified(false);
            }
            budgetInfoMapper.updateSelective(budgetInfo);
        } else if (code.equals(BudgetOperationEnum.ADD.getCode())) {
            BudgetInfoEntity budgetInfo = new BudgetInfoEntity();
            budgetInfo.setBudgetId(budgetId);
            budgetInfo.setUid(uid);
            budgetInfo.setCorpId(addBudgetVo.getCorpId());
            budgetInfo.setBudgetType(BudgetScopeEnum.getEnumByCode(addBudgetVo.getRangeType()).getType());
            budgetInfo.setAmount(addBudgetVo.getBudgetAmount());
            budgetInfo.setWarn(addBudgetVo.getAlarmThreshold());
            budgetInfo.setAmountInit(addBudgetVo.getBudgetAmount());
            budgetInfo.setAmountUsed(new BigDecimal(0));
            budgetInfo.setAmountOccupy(new BigDecimal(0));
            budgetInfo.setIsDeleted(false);
            budgetInfo.setIsActive(true);
            budgetInfo.setIsNotified(false);
            budgetInfo.setStartTime(BudgetDateUtil.getFirstDayOfMonth(addBudgetVo.getStartTime()));
            budgetInfo.setEndTime(BudgetDateUtil.getLastDayOfMonth(addBudgetVo.getEndTime()));
            budgetInfo.setSendMsg(addBudgetVo.isSendMsg());
            budgetInfoMapper.insertSelective(budgetInfo);
        }
    }

    private void saveBudgetGroupEntity(String budgetId, AddBudgetVo addBudgetVo, String code) {
        if (code.equals(BudgetOperationEnum.UPDATE.getCode())) {
            //编辑时预算范围不可改
            return;
        }
        List<BudgetGroupEntity> budgetGroups = Lists.newArrayList();
        for (DepartmentVo departmentVo : addBudgetVo.getDepartment()) {
            BudgetGroupEntity budgetGroup = new BudgetGroupEntity();
            budgetGroup.setBudgetId(budgetId);
            budgetGroup.setCorpId(addBudgetVo.getCorpId());
            budgetGroup.setIsDeleted(Boolean.FALSE);
            budgetGroup.setStartTime(BudgetDateUtil.getFirstDayOfMonth(addBudgetVo.getStartTime()));
            budgetGroup.setEndTime(BudgetDateUtil.getLastDayOfMonth(addBudgetVo.getEndTime()));
            budgetGroup.setScopeId(departmentVo.getDepartId());
            budgetGroup.setScopeName(departmentVo.getDepartName());
            budgetGroup.setScopeType(BudgetScopeEnum.getEnumByCode(addBudgetVo.getRangeType()).getType());
            budgetGroups.add(budgetGroup);
            budgetGroup.setScopeId(departmentVo.getDepartId());
            budgetGroup.setScopeName(departmentVo.getDepartName());
        }
        budgetGroupMapper.insertBatchSelective(budgetGroups);
    }

    private void saveBudgetItem(String budgetId, List<BudgetTypeVo> budgetTypes, String code) {
        List<BudgetItemEntity> budgetItemsForInsert = Lists.newArrayList();
        List<BudgetItemEntity> budgetItemsForUpdate = Lists.newArrayList();
        for (BudgetTypeVo budgetType : budgetTypes) {
            if (budgetType.getId() != null) {
                BudgetItemEntity budgetItem = budgetItemMapper.selectByPrimaryKey(budgetType.getId());
                budgetItem.setAmount(budgetType.getAmount());
                budgetItem.setWarn(budgetType.getThreshold());
                //预算项金额或阀值有变化时，重置告警标记
                if (!BudgetAlarmUtil.isAlarm(budgetItem.getAmount(), budgetItem.getAmountUsed(), budgetItem.getAmountOccupy(), budgetItem.getWarn())) {
                    budgetItem.setIsNotified(false);
                }
                budgetItemsForUpdate.add(budgetItem);
            } else {
                BudgetItemEntity budgetItem = new BudgetItemEntity();
                budgetItem.setBudgetId(budgetId);
                budgetItem.setAmount(budgetType.getAmount());
                budgetItem.setWarn(budgetType.getThreshold());
                budgetItem.setIsAll(budgetType.isIfAll());
                budgetItem.setAmountUsed(new BigDecimal(0));
                budgetItem.setAmountOccupy(new BigDecimal(0));
                budgetItem.setIsNotified(false);
                List<String> types = budgetType.isIfAll() ? Arrays.stream(BudgetTypeEnum.values()).map(BudgetTypeEnum::getType).collect(Collectors.toList()) :
                        budgetType.getTypes().stream().map(TypeDetailVo::getType).collect(Collectors.toList());
                budgetItem.setType(String.join("/", types));
                List<String> typesDesc = budgetType.isIfAll() ? Arrays.stream(BudgetTypeEnum.values()).map(BudgetTypeEnum::getName).collect(Collectors.toList()) :
                        budgetType.getTypes().stream().map(TypeDetailVo::getName).collect(Collectors.toList());
                budgetItem.setTypeDesc(String.join("/", typesDesc));
                budgetItemsForInsert.add(budgetItem);
            }
        }
        if (!CollectionUtils.isEmpty(budgetItemsForInsert)) {
            budgetItemMapper.insertBatchSelective(budgetItemsForInsert);
        }
        if (!CollectionUtils.isEmpty(budgetItemsForUpdate)) {
            budgetItemMapper.updateBatchSelective(budgetItemsForUpdate);
        }
    }

    private void saveBudgetNotify(String budgetId, boolean sendMsg, List<NotifyScopeVo> notifyScopes, String code) {
        if (code.equals(BudgetOperationEnum.UPDATE.getCode())) {
            budgetNotifyMapper.deleteBatchByByBudgetId(budgetId);
        }
        if (sendMsg && !CollectionUtils.isEmpty(notifyScopes)) {
            List<BudgetNotifyEntity> budgetNotifies = Lists.newArrayList();
            for (NotifyScopeVo notifyScopeVo : notifyScopes) {
                BudgetNotifyEntity budgetNotify = new BudgetNotifyEntity();
                budgetNotify.setBudgetId(budgetId);
                budgetNotify.setName(notifyScopeVo.getName());
                budgetNotify.setEmail(notifyScopeVo.getEmail());
                budgetNotifies.add(budgetNotify);
            }
            budgetNotifyMapper.insertBatchSelective(budgetNotifies);
        }
    }

    public void updateAmount(String infoId, Long itemId, BigDecimal amount, String orgName, String type) {
        log.info("需要操作预算表数据: {}, {} ,{} ,{}", infoId, itemId, amount, orgName);

        budgetInfoMapper.updateBudgetAmount(infoId, amount);
        //查询出预算数据计算  是否触发预算预警
        BudgetInfoEntity budgetInfoEntity = budgetInfoMapper.selectByPrimaryKey(infoId);
        List<BudgetNotifyEntity> budgetNotifyEntities = budgetNotifyMapper.selectByBudgetId(infoId);
        List<String> collect = budgetNotifyEntities.stream().map(BudgetNotifyEntity::getEmail).collect(Collectors.toList());

        if (budgetInfoEntity.getWarn().compareTo(BigDecimal.ZERO) != 0) {
            BigDecimal divide = budgetInfoEntity.getWarn().divide(new BigDecimal("100"));
            BigDecimal multiply = budgetInfoEntity.getAmount().multiply(divide);
            if (budgetInfoEntity.getAmount().subtract(budgetInfoEntity.getAmountUsed()).compareTo(multiply) <= 0) {

                String contet = getContet("all", orgName, DateUtil.dateToString(budgetInfoEntity.getStartTime(), DateUtil.DF_YM_CN)
                        , DateUtil.dateToString(budgetInfoEntity.getEndTime(), DateUtil.DF_YM_CN), budgetInfoEntity.getAmount().toString()
                        , budgetInfoEntity.getAmount().subtract(budgetInfoEntity.getAmountUsed()).toString(), budgetInfoEntity.getWarn().toString() + "%");

                BudgetWarningEntity query = new BudgetWarningEntity();
                query.setBudgetId(budgetInfoEntity.getBudgetId());
                query.setBudgetType("all");
                List<BudgetWarningEntity> select = budgetWarningMapper.select(query);
                if (select.size() > 0) {
                    BudgetWarningEntity update = new BudgetWarningEntity();
                    update.setId(select.get(0).getId());
                    update.setContent(contet);
                    budgetWarningMapper.updateByPrimaryKey(update);
                } else {
                    BudgetWarningEntity entity = new BudgetWarningEntity();
                    entity.setBudgetId(budgetInfoEntity.getBudgetId());
                    entity.setBudgetType("all");
                    entity.setContent(contet);
                    entity.setEmall(StringUtils.join(collect, ","));
                    entity.setDatachangeCreatetime(new Date());
                    entity.setDatachangeLasttime(new Date());
                    entity.setIsDeleted(0);
                    budgetWarningMapper.insert(entity);
                }
            }
        }


        if (!Objects.isNull(itemId)) {
            budgetItemMapper.updateBudgetAmount(itemId, amount);
            BudgetItemEntity budgetItemEntity = budgetItemMapper.selectByPrimaryKey(itemId);

            if (budgetItemEntity.getWarn().compareTo(BigDecimal.ZERO) != 0 && !budgetItemEntity.getIsAll()) {
                BigDecimal divide = budgetItemEntity.getWarn().divide(new BigDecimal("100"));
                BigDecimal multiply = budgetItemEntity.getAmount().multiply(divide);
                if (budgetItemEntity.getAmount().subtract(budgetItemEntity.getAmountUsed()).compareTo(multiply) <= 0) {

                    String contet = getContet("sub", orgName, DateUtil.dateToString(budgetInfoEntity.getStartTime(), DateUtil.DF_YM_CN)
                            , DateUtil.dateToString(budgetInfoEntity.getEndTime(), DateUtil.DF_YM_CN), orderMap.get(type), budgetItemEntity.getAmount().toString()
                            , budgetItemEntity.getAmount().subtract(budgetItemEntity.getAmountUsed()).toString(), budgetItemEntity.getWarn().toString() + "%");


                    BudgetWarningEntity query = new BudgetWarningEntity();
                    query.setBudgetId(budgetInfoEntity.getBudgetId());
                    query.setBudgetType("sub");
                    query.setOrderType(type);
                    List<BudgetWarningEntity> select = budgetWarningMapper.select(query);
                    if (select.size() > 0) {
                        BudgetWarningEntity update = new BudgetWarningEntity();
                        update.setId(select.get(0).getId());
                        update.setContent(contet);
                        budgetWarningMapper.updateByPrimaryKey(update);
                    } else {
                        BudgetWarningEntity entity = new BudgetWarningEntity();
                        entity.setBudgetId(budgetItemEntity.getBudgetId());
                        entity.setBudgetType("sub");
                        entity.setContent(contet);
                        entity.setEmall(StringUtils.join(collect, ","));
                        entity.setOrderType(type);
                        entity.setDatachangeCreatetime(new Date());
                        entity.setDatachangeLasttime(new Date());
                        entity.setIsDeleted(0);
                        budgetWarningMapper.insert(entity);
                    }
                }
            }
        }
    }

    public String getContet(String type,Object... org) {
        String all = "%s的%s-%s月的预算总金额额为¥%s，目前剩余预算金额为¥%s，已不足%s，请您及时关注。";
        String sub = "%s 的%s-%s的%s预算总金额额为¥%s，目前剩余预算金额为¥%s，已不足%s，请您及时关注。";
        if (Objects.equals("all", type)) {
            return String.format(all, org);
        } else {
            return String.format(sub, org);
        }
    }
}